using System.Collections.Generic;
using System.Linq;
using System.Web;
using MvcCms.Data;
using System.Web.Routing;
using MvcCms.Data.Code;
using System;
using System.Net;
using System.Net.Sockets;

namespace MvcCms.Core.Cache
{
    public class CacheService : ICacheService
    {
        private const string CategoriesHolder = "Categories";
        private const string PortalsHolder = "Portals";
        private const string SubPagesHolder = "SubPages";
        private const string AltLanguagePagesHolder = "AltLanguagePages";

        #region Constructors

        private readonly ICmsRepository _cmsRepository;
        private readonly IInMemoryCache _inmemorycacheService;
        private readonly IPortalRepository _portalRepository;   

        public CacheService(IPortalRepository portalRepository, IInMemoryCache inmemorycacheService, ICmsRepository cmsRepository)
        {
            _portalRepository = portalRepository;
            _inmemorycacheService = inmemorycacheService;
            _cmsRepository = cmsRepository;
        }

        #endregion

        public Portal GetCurrentPortal()
        {
            //if httpcontext is null this is a unit test so just pick the default portal
            if (HttpContext.Current == null)
            {
                IEnumerable<Portal> portals = _inmemorycacheService.Get(PortalsHolder, () => _portalRepository.ListPortals());
                return portals.Where(portal => portal.Name.Equals("Default")).FirstOrDefault();
            }
            else
            {
                string url = "";
                try
                {
                    var ip = string.Empty;
                    var urlHost = HttpContext.Current.Request.Url.Host.ToLower();
                    //also check for this in case iis express is being used with the computer name or ip address
                    var computerHost = System.Net.Dns.GetHostName().ToLower();
                    var ipv4Addresses = Array.FindAll(Dns.GetHostEntry(string.Empty).AddressList, a => a.AddressFamily == AddressFamily.InterNetwork);
                    if (ipv4Addresses.Length > 0) { ip = ipv4Addresses.First().ToString(); }

                    //if this is a localhost request we want the port if it is not usually we don't, if you do want a non localhost port do it here.
                    if (urlHost != "localhost" && urlHost != computerHost && urlHost != ip)
                    {
                        url = HttpContext.Current.Request.Url.Host.ToLower();
                    }
                    else
                    {
                        url = HttpContext.Current.Request.Url.Authority.ToLower();
                    }   
                    IEnumerable<Portal> portals = _inmemorycacheService.Get(PortalsHolder, () => _portalRepository.ListPortals());
                    return portals.Where(portal => portal.URLAuthority.Equals(url)).FirstOrDefault();
                }
                catch
                {
                    IEntitiesProvider entityprovider = new EntitiesProvider();
                    entityprovider.ReopenConnection();

                    var context = entityprovider.GetEntityContext();
                    using (context)
                    {
                        url = HttpContext.Current.Request.Url.Authority.ToLower();
                        IEnumerable<Portal> portals = _inmemorycacheService.Get(PortalsHolder, () => _portalRepository.ListPortals());
                        return portals.Where(portal => portal.URLAuthority.Equals(url)).FirstOrDefault();
                    }
                }
            }

        }

        #region Cms Methods

        public ContentPage GetPublishedCategoryContentPage(int routeCatId)
        {
            int correctCatId = GetCorrectCategoryId(routeCatId);
            return _cmsRepository.GetPublishedCatContentPageByCategory(correctCatId);
        }

        public ContentCategory GetCurrentCategory()
        {
            var httpContext = new HttpContextWrapper(HttpContext.Current);
            var routeData = RouteTable.Routes.GetRouteData(httpContext);

            int categoryId = 0;
            var oCategoryID = routeData.DataTokens["contentcatid"];
            if (oCategoryID != null)
            {
                categoryId = int.Parse(oCategoryID.ToString());
                var currentPathName = (string)routeData.Values["path"];
                return GetCorrectCategory(categoryId, currentPathName);
            }
            return null;
        }

        public ContentCategory GetCorrectCategory(int routeCatId, string routePath)
        {
            IEnumerable<ContentCategory> categories = _inmemorycacheService.Get(CategoriesHolder,
                            () => _cmsRepository.ListAllCategories());
            Portal currentPortal = GetCurrentPortal();

            ContentCategory portalCat =
                categories.Where(category => category.Portal.PortalID
                            .Equals(currentPortal.PortalID) &&
                            category.CategoryID.Equals(routeCatId)
                            ).FirstOrDefault();

            if (portalCat == null)
            {
                //the categoryid passed in as a route parameter is from another portal
                //sharing the same route path.  This is ok, we will get the single path
                //from the route category and find that path in our current portals
                //set of categories

                //get the category passed in the route
                ContentCategory routeCat = categories.Where(category => category.CategoryID
                              .Equals(routeCatId)).SingleOrDefault();

                //save its singlepath value
                string routeSinglePath = routeCat.SinglePath;

                //find that singlepath value in the 
                //current portal's list of categories, 
                //this will allow portals to share routes.
                portalCat = categories.Where(category => category.Portal.PortalID
                                .Equals(currentPortal.PortalID) &&
                                category.SinglePath.Equals(routeSinglePath)).
                                FirstOrDefault();

            }
            //now we have the right cat, check if the path is a subcat
            if (routePath != null)
            {
                string portalCatPath = portalCat.Path;
                portalCat = categories.Where(category => category.Portal.PortalID
                                .Equals(currentPortal.PortalID) &&
                                category.Path.Equals(portalCatPath + "/" + routePath)).
                                FirstOrDefault();
            }
            if (portalCat == null)
            {
                int correctCatId = GetCorrectCategoryId(routeCatId);
                portalCat = categories.Where(category => category.CategoryID == correctCatId).FirstOrDefault();
            }
            return portalCat;
        }

        private int GetCorrectCategoryId(int routeCatId)
        {
            IEnumerable<ContentCategory> categories = _inmemorycacheService.Get(CategoriesHolder,
                                                                                () => _cmsRepository.ListAllCategories());
            Portal currentPortal = GetCurrentPortal();

            IEnumerable<ContentCategory> portalcats =
                categories.Where(category => category.Portal.PortalID
                                                 .Equals(currentPortal.PortalID) &&
                                             category.CategoryID.Equals(routeCatId)
                    );

            if (portalcats.Count() == 0)
            {
                //the categoryid passed in as a route parameter is from another portal
                //sharing the same route path.  This is ok, we will get the single path
                //from the route category and find that path in our current portals
                //set of categories

                //get the category passed in the route
                ContentCategory routeCat = categories.Where(category => category.CategoryID
                                                                            .Equals(routeCatId)).SingleOrDefault();

                //save its singlepath value
                string routeSinglePath = routeCat.SinglePath;

                //find that singlepath value in the 
                //current portal's list of categories, 
                //this will allow portals to share routes.

                //we are going to check against the full path since there can only be one first level
                //url slug per portal
                ContentCategory portalCat = categories.Where(category => category.Portal.PortalID
                             .Equals(currentPortal.PortalID) &&
                         category.Path.Equals(routeSinglePath)).SingleOrDefault();


                //Now we have the correct category id, lets just pull the page from the repository
                return portalCat.CategoryID;
            }
            //The Correct category id was passed in lets get the page
            return routeCatId;
        }

        public ContentPage GetContentSubPageByPath(string path, int routeCatId)
        {
            int correctCatId = GetCorrectCategoryId(routeCatId);

            var categorySubPages = ListSubPages(correctCatId);

            return categorySubPages.Where(page => page.Path == path).FirstOrDefault();

        }

        public ContentPage GetPublishedSubCategoryContentPage(string path)
        {
            Portal currentPortal = GetCurrentPortal();
            IEnumerable<ContentCategory> categories = _inmemorycacheService.Get(CategoriesHolder,
                                                                                () => _cmsRepository.ListAllCategories());

            ContentCategory subcat = categories.Where(cat => cat.Portal.PortalID == currentPortal.PortalID &&
                                                             "/" + cat.Path == path).FirstOrDefault();
            if (subcat != null)
            {
                return GetPublishedCategoryContentPage(subcat.CategoryID);
            }
            else
            {
                return null;
            }
        }

        public IEnumerable<ContentCategory> ListTopNav()
        {
            Portal currentPortal = GetCurrentPortal();
            IEnumerable<ContentCategory> allCats = _inmemorycacheService.Get(CategoriesHolder,
                                                                             () => _cmsRepository.ListAllCategories());

            return allCats.Where(cat => cat.ParentCategory == null &&
                                        cat.Portal.PortalID.Equals(currentPortal.PortalID));
        }

        public IList<ContentCategory> ListCategoriesHierarchy()
        {
            Portal currentPortal = GetCurrentPortal();
            IEnumerable<ContentCategory> allCats = _inmemorycacheService.Get(CategoriesHolder,
                                                                             () => _cmsRepository.ListAllCategories());
            return allCats.Where(cat => cat.Portal.PortalID.Equals(currentPortal.PortalID)
                                        && cat.ParentCategory == null).OrderBy(cat => cat.Importance).ToList();

        }

        public IEnumerable<ContentPage> ListSubPages(int catId)
        {
            int correctCatId = GetCorrectCategoryId(catId);
            IEnumerable<ContentPage> subPages = _inmemorycacheService.Get(SubPagesHolder,
                                                                          () => _cmsRepository.ListSubPages());

            return subPages.Where(page => page.ContentCategory.CategoryID == correctCatId);
        }

        public IEnumerable<ContentPage> ListAltLanguagePages(int parentPageId)
        {
            IEnumerable<ContentPage> altLanguagePages = _inmemorycacheService.Get(AltLanguagePagesHolder,
                                                                          () => _cmsRepository.ListAltLanguagePages());

            return altLanguagePages.Where(page => page.ParentLanguageVersion.ContentPageID.Equals(parentPageId));
        }

        #endregion
    }
}